package net.vkits.auth.domain.model;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import net.vkits.common.BaseModel;
import org.apache.shiro.crypto.RandomNumberGenerator;
import org.apache.shiro.crypto.SecureRandomNumberGenerator;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.util.ByteSource;

import javax.persistence.*;
import java.util.Date;
import java.util.LinkedHashSet;
import java.util.Set;

/**
 * Created by ethan-liu on 16/3/15.
 */
@Entity
@Table(name = "sys_user")
public class User extends BaseModel {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(unique = true, nullable = false)
    private String username;
    private String password;
    private String salt;
    private Boolean deleted = Boolean.FALSE;
    private Boolean admin = Boolean.FALSE;

    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "create_time", nullable = false)
    private Date createTime = new Date();

    @Enumerated(EnumType.STRING)
    private UserStatus status = UserStatus.normal;

    @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH}, fetch = FetchType.EAGER)
    @JoinTable(name = "sys_user_role",
            joinColumns = {@JoinColumn(name = "user_id")},
            inverseJoinColumns = {@JoinColumn(name = "role_id")})
    private Set<Role> roles = new LinkedHashSet<>();

    @JsonIgnore
    public String getCredentialsSalt() {
        return username + salt;
    }

    private void setPassword(String password) {
        String algorithmName = "md5";
        final int hashIterations = 2;
        this.setSalt();
        String newPassword = new SimpleHash(
                algorithmName,
                password,
                ByteSource.Util.bytes(this.getCredentialsSalt()),
                hashIterations).toHex();
        this.password = newPassword;
    }

    private void setSalt() {
        RandomNumberGenerator randomNumberGenerator = new SecureRandomNumberGenerator();
        this.salt = randomNumberGenerator.nextBytes().toHex();
    }

    public boolean isLocked() {
        return this.status == UserStatus.blocked;
    }

    @JsonIgnore
    public boolean isAdmin() {
        return this.admin;
    }

    public void setAdmin() {
        this.admin = Boolean.TRUE;
    }

    public Long getId() {
        return id;
    }

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    public Date getCreateTime() {
        return createTime == null ? null : (Date)createTime.clone();
    }

    public UserStatus getStatus() {
        return status;
    }

    public String getUsername() {
        return username;
    }

    @JsonIgnore
    public String getPassword() {
        return password;
    }

    public void changePassword(String newPassword) {
        this.setPassword(newPassword);
    }

    public Set<Role> getRoles() {
        return roles;
    }

    public void setRoles(Set<Role> roles) {
        this.roles = roles;
    }

    public User(String username, String password) {
        this.username = username;
        this.setPassword(password);
    }

    public void deletedUser() {
        this.deleted = true;
    }

    public void blockUser() {
        this.status = UserStatus.blocked;
    }

    public void unblockUser() {
        this.status = UserStatus.normal;
    }

    // Just for JPA
    private User() {
    }
}
